package com.fast.alden.admin.service.impl;

import com.fast.alden.admin.service.SysMenuService;
import com.fast.alden.admin.service.SysRoleService;
import com.fast.alden.common.exception.BizException;
import com.fast.alden.common.exception.BizExceptionEnum;
import com.fast.alden.common.service.impl.BaseTreeNodeEntityServiceImpl;
import com.fast.alden.data.model.*;
import com.fast.alden.data.repo.SysMenuRepository;
import com.fast.alden.data.repo.SysMenuRoleRepository;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SysMenuServiceImpl extends BaseTreeNodeEntityServiceImpl<SysMenu, SysMenuRepository> implements SysMenuService {
    @Resource
    private SysMenuRepository repository;
    @Resource
    private SysMenuRoleRepository menuRoleRepository;
    @Resource
    private SysRoleService roleService;

    @Override
    protected Class<SysMenu> getEClass() {
        return SysMenu.class;
    }

    @Override
    protected SysMenuRepository getRepository() {
        return repository;
    }

    @Override
    @Transactional
    public SysMenu create(SysMenu entity) {
        if (entity == null) {
            throw new BizException(BizExceptionEnum.ENTITY_IS_NULL);
        }
        check(entity);

        return super.create(entity);
    }

    @Override
    @Transactional
    public SysMenu update(SysMenu entity) {
        if (entity == null) {
            throw new BizException(BizExceptionEnum.ENTITY_IS_NULL);
        }
        if (entity.getId() == null) {
            throw new BizException(BizExceptionEnum.ENTITY_ID_IS_NULL);
        }
        check(entity);

        return super.update(entity);
    }

    private void check(SysMenu entity) {
        // 菜单类型不可为空
        if (entity.getType() == null) {
            throw new BizException(BizExceptionEnum.MENU_TYPE_MUST_BE_NOT_NULL);
        }
        // 按钮父级不可为空
        if (entity.getType().equals(SysMenuType.BUTTON) && entity.getParentId() == null) {
            throw new BizException(BizExceptionEnum.THE_PARENT_OF_BUTTON_MUST_BE_NOT_NULL);
        }
        if (entity.getType().equals(SysMenuType.MENU)) {
            // 菜单模式不可为空
            if (entity.getMode() == null) {
                throw new BizException(BizExceptionEnum.THE_MODE_OF_MENU_MUST_BE_NOT_NULL);
            }
            if (entity.getMode().equals(SysMenuMode.EXTERNAL_LINK) || entity.getMode().equals(SysMenuMode.INTERNAL_LINK)) {
                // URL不可为空
                if (!StringUtils.hasText(entity.getUrl())) {
                    throw new BizException(BizExceptionEnum.THE_URL_OF_MENU_MUST_BE_NOT_NULL);
                }
            }
            if (entity.getMode().equals(SysMenuMode.SYNC_ROUTE) || entity.getMode().equals(SysMenuMode.ASYNC_ROUTE)) {
                // 路由不可为空
                if (!StringUtils.hasText(entity.getRoute())) {
                    throw new BizException(BizExceptionEnum.THE_ROUTE_OF_MENU_MUST_BE_NOT_NULL);
                }
                // 路由不可重复
                if (repository.findByTypeEqualsAndRouteEquals(SysMenuType.MENU, entity.getRoute()).isPresent()) {
                    throw new BizException(BizExceptionEnum.THE_ROUTE_OF_MENU_MUST_BE_UNIQUE);
                }
                // 异步路由部署路径不可为空
                if (entity.getMode().equals(SysMenuMode.ASYNC_ROUTE) && !StringUtils.hasText(entity.getPath())) {
                    throw new BizException(BizExceptionEnum.THE_PATH_OF_ASYNC_MENU_MUST_BE_NOT_NULL);
                }
            }
        }
    }

    @Override
    public List<SysMenu> tree() {
        List<SysMenu> list = this.list();
        list.sort(Comparator.comparing(SysMenu::getOrderNo, Comparator.nullsLast(Comparator.naturalOrder())));
        return buildTreeList(list);
    }

    @Override
    @Transactional
    public List<SysMenu> remove(List<Long> ids) {
        List<SysMenu> menus = super.remove(ids);

        // 删除菜单-角色关联数据
        menuRoleRepository.deleteByMenuIdIn(ids);

        return menus;
    }

    @Override
    @Transactional
    public void assign(Long roleId, List<Long> menuIds) {
        SysRole role = roleService.retrieveWithException(roleId);

        Set<Long> allMenuIdsInIdPath = new HashSet<>();
        list(menuIds).forEach(menu -> {
            allMenuIdsInIdPath.addAll(Arrays.stream(menu.getIdPath().split(ITreeNode.ID_PATH_SEPARATOR)).filter(StringUtils::hasText).map(Long::valueOf).toList());
        });
        menuIds = allMenuIdsInIdPath.stream().toList();

        List<SysMenuRole> existList = menuRoleRepository.findAllByRoleIdIsAndMenuIdIn(roleId, menuIds);
        List<Long> existMenuIds = existList.stream().map(SysMenuRole::getMenuId).toList();

        List<Long> toAddMenuIds = menuIds.stream().filter(it -> !existMenuIds.contains(it)).toList();

        if (toAddMenuIds.isEmpty()) {
            return;
        }

        List<SysMenuRole> toAddList = toAddMenuIds.stream().map(it -> {
            SysMenuRole menuRole = new SysMenuRole();
            menuRole.setRoleId(roleId);
            menuRole.setMenuId(it);
            return menuRole;
        }).toList();

        menuRoleRepository.saveAll(toAddList);
    }

    @Override
    @Transactional
    public void cancelAssign(Long roleId, List<Long> menuIds) {
        SysRole role = roleService.retrieveWithException(roleId);

        List<SysMenuRole> existList = menuRoleRepository.findAllByRoleIdIsAndMenuIdIn(roleId, menuIds);
        if (existList.isEmpty()) {
            return;
        }

        menuRoleRepository.deleteAll(existList);
    }

    @Override
    public List<SysMenu> findMenusByRoleId(Long roleId) {
        List<SysMenuRole> menuRoles = menuRoleRepository.findAllByRoleIdIn(List.of(roleId));
        List<Long> menuIds = menuRoles.stream().map(SysMenuRole::getMenuId).toList();
        return this.list(menuIds);
    }

    @Override
    public List<SysMenu> findAllByRoleIds(List<Long> roleIds) {
        List<SysMenuRole> menuRoles = menuRoleRepository.findAllByRoleIdIn(roleIds);

        return this.list(menuRoles.stream().map(SysMenuRole::getMenuId).toList());
    }

    @Override
    public List<SysMenu> treeListByRoleIds(List<Long> roleIds) {
        return this.buildTreeList(this.findAllByRoleIds(roleIds));
    }

    @Override
    protected void setParent(SysMenu entity) {
        if (entity.getType().equals(SysMenuType.BUTTON) && entity.getParentId() == null) {
            throw new BizException(BizExceptionEnum.THE_PARENT_OF_BUTTON_MUST_BE_MENU);
        }
        if (entity.getParentId() != null) {
            SysMenu parent = this.retrieveWithException(entity.getParentId());
            if (entity.getType().equals(SysMenuType.DIRECTORY) && !parent.getType().equals(SysMenuType.DIRECTORY)) {
                throw new BizException(BizExceptionEnum.THE_PARENT_OF_DIRECTORY_MUST_BE_DIRECTORY_OR_NULL);
            }
            if (entity.getType().equals(SysMenuType.MENU) && !parent.getType().equals(SysMenuType.DIRECTORY)) {
                throw new BizException(BizExceptionEnum.THE_PARENT_OF_MENU_MUST_BE_DIRECTORY_OR_NULL);
            }
            if (entity.getType().equals(SysMenuType.BUTTON) && !parent.getType().equals(SysMenuType.MENU)) {
                throw new BizException(BizExceptionEnum.THE_PARENT_OF_BUTTON_MUST_BE_MENU);
            }
        }
        super.setParent(entity);

    }
}
